<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import { initCamera, initWebGLRenderer, initStat } from "@/book/ThreeUtils";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";

// 画布
const canvas = ref<any>('');
// 场景
const scene = new THREE.Scene();
// 相机
const camera = initCamera(undefined);
// 渲染器
const renderer = initWebGLRenderer({});
// 性能统计
const stat = initStat(0);
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
orbitControl.update();
// 环境光
// 颜色将会应用到全局。该光源并没有特别的来源方向，并且THREE.AmbientLight不会生成阴影。
// 通常，不能将THREE.AmbientLight作为场景中唯一的光源，因为它会将场景中的所有物体渲染为相同的颜色，而不管是什么形状
// 在使用其他光源（如THREE.SpotLight或THREE.DirectionalLight）的同时使用它，目的是弱化阴影或给场景添加一些额外的颜色。
const ambient = new THREE.AmbientLight("#606008", 1);
scene.add(ambient);
// 聚光灯
const spot = new THREE.SpotLight(0xffffff, 1, 180, Math.PI / 4);
spot.shadow.mapSize.set(window.innerWidth, window.innerHeight);
spot.position.set(-30, 40, -10);
spot.castShadow = true;
scene.add(spot);
// 添加房子和树
createBoundingWall();
createGroundPlane();
createHouse();
createTree();
// gui
const controlObj = {
  intensity: ambient.intensity, // 环境光光照强度
  ambientColor: ambient.color.getStyle(), // 环境光颜色
  disableSpotlight: false, // 是否禁用聚光灯
};
const gui = new GUI();
gui.add(controlObj, 'intensity', 0, 3, 0.1).name("环境光光照强度").onChange(() => {
  ambient.color = new THREE.Color(controlObj.ambientColor);
  ambient.intensity = controlObj.intensity;
});
gui.addColor(controlObj, 'ambientColor').name("环境光颜色").onChange(() => {
  ambient.color = new THREE.Color(controlObj.ambientColor);
  ambient.intensity = controlObj.intensity;
});
// @ts-ignore
gui.add(controlObj, 'disableSpotlight').name("禁用聚光灯").onChange((e) => {
  spot.visible = !e;
});
// 渲染函数
const render = () => {
  stat.update();

  renderer.render(scene, camera);
  requestAnimationFrame(render);
}
// 挂载
onMounted(() => {
  canvas.value.appendChild(stat.domElement);
  canvas.value.appendChild(renderer.domElement);
  render();
});


/**
 * 创建边界墙
 */
function createBoundingWall() {
  // 墙体
  const wallLeft = new THREE.BoxGeometry(70, 2, 2);
  const wallRight = new THREE.BoxGeometry(70, 2, 2);
  const wallTop = new THREE.BoxGeometry(2, 2, 50);
  const wallBottom = new THREE.BoxGeometry(2, 2, 50);
  // 墙体材质
  const wallMaterial = new THREE.MeshLambertMaterial({
    color: 0xa0522d,
    side: THREE.DoubleSide,
  });
  // 墙体网格模型Mesh
  const wallLeftMesh = new THREE.Mesh(wallLeft, wallMaterial);
  const wallRightMesh = new THREE.Mesh(wallRight, wallMaterial);
  const wallTopMesh = new THREE.Mesh(wallTop, wallMaterial);
  const wallBottomMesh = new THREE.Mesh(wallBottom, wallMaterial);
  // 墙体position设置
  wallLeftMesh.position.set(15, 1, -25);
  wallRightMesh.position.set(15, 1, 25);
  wallTopMesh.position.set(-19, 1, 0);
  wallBottomMesh.position.set(49, 1, 0);

  scene.add(wallLeftMesh, wallRightMesh, wallTopMesh, wallBottomMesh);
}

/**
 * 创建地面
 */
function createGroundPlane() {
  // 地面
  const planeGeometry = new THREE.PlaneGeometry(70, 50);
  const planeMaterial = new THREE.MeshLambertMaterial({
    color: 0x9acd32,
    side: THREE.DoubleSide,
  });
  const plane = new THREE.Mesh(planeGeometry, planeMaterial);
  plane.receiveShadow = true;
  plane.rotation.x = -0.5 * Math.PI;
  plane.position.set(15, 0, 0);
  scene.add(plane);
}

/**
 * 创建房子
 */
function createHouse() {
  // 房顶
  const roof = new THREE.ConeGeometry(5, 4);
  const roofMesh = new THREE.Mesh(roof, new THREE.MeshLambertMaterial({
    color: 0x8b7213,
  }));
  // 房子主体
  const house = new THREE.CylinderGeometry(5, 5, 6);
  const houseMesh = new THREE.Mesh(house, new THREE.MeshLambertMaterial({
    color: 0xffe4c4,
  }));

  roofMesh.position.set(25, 8, 0);
  houseMesh.position.set(25, 3, 0);

  roofMesh.receiveShadow = true;
  houseMesh.receiveShadow = true;
  roofMesh.castShadow = true;
  houseMesh.castShadow = true;

  scene.add(roofMesh, houseMesh);
}

/**
 * 创建树
 */
function createTree() {
  // 树干
  const trunk = new THREE.BoxGeometry(1, 8, 1);
  const trunkMesh = new THREE.Mesh(trunk, new THREE.MeshLambertMaterial({
    color: 0x8b4513,
  }));
  // 树叶
  const leave = new THREE.SphereGeometry(4);
  const leaveMesh = new THREE.Mesh(leave, new THREE.MeshLambertMaterial({
    color: 0x00ff00,
  }));

  trunkMesh.position.set(-10, 4, 0);
  leaveMesh.position.set(-10, 12, 0);

  trunkMesh.receiveShadow = true;
  trunkMesh.castShadow = true;
  leaveMesh.receiveShadow = true;
  leaveMesh.castShadow = true;

  scene.add(trunkMesh, leaveMesh);
}
</script>
